home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / !FednetCmp / c / Scan < prev    next >
Encoding:
Text File  |  2003-10-23  |  30.0 KB  |  871 lines

  1. /*
  2.  *  FednetCmp - Fednet file compression/decompression 
  3.  *  Directory scan
  4.  *  Copyright (C) 2001  Chris Bazley
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public Licence as published by
  8.  *  the Free Software Foundation; either version 2 of the Licence, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public Licence for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public Licence
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /* ANSI library files */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdbool.h>
  26.  
  27. /* RISC OS library files */
  28. #include "wimp.h"
  29. #include "kernel.h"
  30. #include "swis.h"
  31. #include "toolbox.h"
  32. #include "event.h"
  33. #include "wimplib.h"
  34. #include "window.h"
  35. #include "gadgets.h"
  36. #include "flex.h"
  37.  
  38. /* My library files */
  39. #include "err.h"
  40. #include "msgtrans.h"
  41. #include "FedCompMT.h"
  42. #include "ViewsMenu.h"
  43. #include "Loader.h"
  44. #include "Macros.h"
  45. #include "RoundRobin.h"
  46. #include "LoadSaveMT.h"
  47. #include "AbortFop.h"
  48.  
  49. /* Local headers */
  50. #include "Utils.h"
  51. #include "Main.h"
  52. #include "Scan.h"
  53.  
  54. /* gadget IDs */
  55. #define SCAN_ACTIVITY     0x0c
  56. #define SCAN_PATH         0x0d
  57. #define SCAN_PROGRESS     0x0e
  58. #define SCAN_CONVERTED    0x0b
  59. #define SCAN_ABORT        0x01
  60. #define SCAN_SKIP         0x02
  61. #define SCAN_RESTART      0x03
  62. #define SCAN_FOURTHBUTTON 0x04
  63. #define SCAN_MESSAGE      0x05
  64.  
  65. #define SCAN_PHASE_DUMMY     -3
  66. #define SCAN_PHASE_ERROR     -2
  67. #define SCAN_PHASE_PAUSE     -1
  68. #define SCAN_PHASE_IDLE      0
  69. #define SCAN_PHASE_SCANNING  1
  70. #define SCAN_PHASE_LOAD      2
  71. #define SCAN_PHASE_SAVE      5
  72.  
  73. typedef struct
  74. {  int loadaddr;
  75.    int execaddr;
  76.    int length;
  77.    int attributes;
  78.    int objecttype;
  79.    int filetype;
  80.    char filename[256];
  81. } OSGBPBblock;
  82.  
  83. typedef struct _ScanData
  84. {
  85.   ObjectId window_id; /* dialogue window */
  86.   char *loadroot;
  87.   char *saveroot;
  88.   int phase; /* what is going on */
  89.   int level;
  90.   int numchecked;
  91.   int numoutput;
  92.  
  93.   char *loadpath;
  94.  
  95.   void *buffer; /* flex block */
  96.  
  97.   bool compress; /* action */
  98.   bool single_file; /* are we doing a single file, or a dir scan? */
  99.   int comp_type; /* file type to give compressed output */
  100.  
  101.   char *savepath; /* "root"+"relative_dir"+"leafname" */
  102.  
  103.   char *relativedir;
  104.  
  105.   int *position; /* position on each level of tree */
  106.  
  107.   int return_phase; /* for pause, error */
  108.   /* preserved data for retry */
  109.   int retry_numchecked;
  110.   int retry_numoutput;
  111.   int retry_position;
  112.  
  113.   FILE **thread_state;
  114.   char return_action[16];
  115. } ScanData;
  116.  
  117. /* ----------------------------------------------------------------------- */
  118. /*                       Function prototypes                               */
  119.  
  120. static void _Scan_displayerror(ScanData *scan_data, char *errormessage);
  121. static void _Scan_displayprogress(ScanData *scan_data);
  122. static void _Scan_updatewindow(ScanData *scan_data, const char *action, const char *filepath);
  123. static int _Scan_scanforfile(ScanData *scan_data);
  124. static int _Scan_loadfile(ScanData *scan_data, const volatile bool *time_up);
  125. static int _Scan_savefile(ScanData *scan_data, const volatile bool *time_up);
  126. static RoundRobinHandler _Scan_pollhandler;
  127. static ToolboxEventHandler _Scan_buttonhandler, _Scan_deletedhandler;
  128.  
  129. /* ----------------------------------------------------------------------- */
  130. /*                         Public functions                                */
  131.  
  132. ObjectId Scan_create(char *newloadroot, char *newsaveroot, bool compress, int comp_type)
  133. {
  134.   char *titletok, *listtok;
  135.   ScanData *newscan_data;
  136.  
  137.   /* Allocate space for thread data */
  138.   newscan_data = malloc(sizeof(ScanData));
  139.   if(newscan_data == NULL)
  140.     MG_RETV("NoMem", NULL_ObjectId) /* failure */
  141.  
  142.   /* Create scan progress window */
  143.   if(E(toolbox_create_object(0, "Scan", &newscan_data->window_id)))
  144.     goto errexit1;
  145.  
  146.   /* Add to window list */
  147.   if(compress) {
  148.     titletok = "ScanCompTitle";
  149.     listtok = "ScanCompList";
  150.   }
  151.   else {
  152.     titletok = "ScanDeCompTitle";
  153.     listtok = "ScanDeCompList";
  154.   }
  155.   if(E(window_set_title(0, newscan_data->window_id, msgs_lookup(titletok))))
  156.     goto errexit2;
  157.   if(E(ViewsMenu_add(newscan_data->window_id, msgs_lookup_sub1(listtok, tail(newloadroot, 3)), "")))
  158.     goto errexit2;
  159.  
  160.   /* register to receive null polls */
  161.   if(E(RoundRobin_register(_Scan_pollhandler, newscan_data)))
  162.     goto errexit3;
  163.  
  164.   /* Allocate memory */
  165.   newscan_data->loadpath = NULL;
  166.   newscan_data->savepath = NULL;
  167.   newscan_data->loadroot = copystring(newloadroot);
  168.   if(newscan_data->loadroot == NULL)
  169.     goto errexit4;
  170.   newscan_data->saveroot = copystring(newsaveroot);
  171.   if(newscan_data->saveroot == NULL)
  172.     goto errexit5;
  173.   newscan_data->relativedir = copystring("");
  174.   if(newscan_data->relativedir == NULL)
  175.     goto errexit6;
  176.   newscan_data->position = malloc(sizeof(int));
  177.   if(newscan_data->position == NULL)
  178.     goto errexit7;
  179.  
  180.   /* Unallocated flex anchor */
  181.   newscan_data->buffer = NULL;
  182.  
  183.   /* Are we doing a batch scan or a single file? */
  184.   _kernel_osfile_block inout; /* not sure if this is necessary */
  185.   switch(_kernel_osfile(17, newloadroot, &inout)) {
  186.     case _kernel_ERROR:
  187.       err_check_rep(_kernel_last_oserror());
  188.       goto errexit8;
  189.  
  190.     case 1: /* single file operation */
  191.       newscan_data->single_file = true;
  192.       newscan_data->thread_state = NULL;
  193.       newscan_data->phase = SCAN_PHASE_LOAD;
  194.       break;
  195.  
  196.     case 2: /* scan inside directory */
  197.     case 3: /* or image file (can this ever happen?) */
  198.       newscan_data->single_file = false;
  199.       newscan_data->phase = SCAN_PHASE_SCANNING;
  200.       break;
  201.  
  202.     default: /* Unknown object type or not found */
  203.       goto errexit8;
  204.   }
  205.  
  206.   /* Initialise thread variables */
  207.   newscan_data->level = 0;
  208.   newscan_data->position[newscan_data->level] = 0;
  209.   newscan_data->numchecked = 0;
  210.   newscan_data->numoutput = 0;
  211.   newscan_data->compress = compress;
  212.   newscan_data->comp_type = comp_type;
  213.  
  214.   /* Register event handlers
  215.      Note that ObjectDeleted handler is registered AFTER anything that could cause an error and therefore premature deletion!
  216.   */
  217.   if(!E(event_register_toolbox_handler(newscan_data->window_id, ActionButton_Selected, _Scan_buttonhandler, newscan_data))
  218.   && !E(event_register_toolbox_handler(newscan_data->window_id, Toolbox_ObjectDeleted,  _Scan_deletedhandler, newscan_data))) {
  219.  
  220.     /* Set up window */
  221.     _Scan_displayprogress(newscan_data);
  222.     if(newscan_data->single_file)
  223.       _Scan_updatewindow(newscan_data, msgs_lookup("ScanTLoad"), newloadroot);
  224.     else
  225.       _Scan_updatewindow(newscan_data, msgs_lookup("ScanTOpen"), newloadroot);
  226.     RE(button_set_value(0, newscan_data->window_id, SCAN_CONVERTED, "0"))
  227.  
  228.     return newscan_data->window_id; /* initialisation successful */
  229.   }
  230.   errexit8:
  231.     free(newscan_data->position);
  232.   errexit7:
  233.     free(newscan_data->relativedir);
  234.   errexit6:
  235.     free(newscan_data->saveroot);
  236.   errexit5:
  237.     free(newscan_data->loadroot);
  238.   errexit4:
  239.     RoundRobin_deregister(_Scan_pollhandler, newscan_data);
  240.   errexit3:
  241.     RE(ViewsMenu_remove(newscan_data->window_id))
  242.   errexit2:
  243.     RE(toolbox_delete_object(0, newscan_data->window_id))
  244.   errexit1:
  245.     free(newscan_data);
  246.     return NULL_ObjectId; /* failure */
  247. }
  248.  
  249. /* ----------------------------------------------------------------------- */
  250. /*                         Private functions                               */
  251.  
  252. static void _Scan_pollhandler(void *handle, const volatile bool *time_up)
  253. {
  254.   ScanData *scan_data = (ScanData *)handle;
  255.   int new_phase = SCAN_PHASE_DUMMY;
  256.  
  257.   err_suppress_errors(); /* suppress and record errors */
  258.  
  259.   switch(scan_data->phase) {
  260.     case SCAN_PHASE_SCANNING:
  261.       new_phase = _Scan_scanforfile(scan_data);
  262.       break;
  263.  
  264.     case SCAN_PHASE_LOAD:
  265.       new_phase = _Scan_loadfile(scan_data, time_up);
  266.       break;
  267.  
  268.     case SCAN_PHASE_SAVE:
  269.       new_phase = _Scan_savefile(scan_data, time_up);
  270.       break;
  271.   }
  272.  
  273.   if(new_phase != SCAN_PHASE_DUMMY)
  274.     scan_data->phase = new_phase;
  275.  
  276.   if(scan_data->phase == SCAN_PHASE_ERROR) {
  277.     /* free any allocated buffers */
  278.     if(scan_data->buffer != NULL)
  279.       flex_free((flex_ptr)&scan_data->buffer);
  280.  
  281.     /* turn progress window into error box */
  282.     _Scan_displayerror(scan_data, err_dump_suppressed()->errmess);
  283.     RoundRobin_deregister(_Scan_pollhandler, handle); /* cease null-polling */
  284.   }
  285.   else
  286.     err_dump_suppressed(); /* Allow up-front errors again */
  287. }
  288.  
  289. /* ----------------------------------------------------------------------- */
  290.  
  291. static int _Scan_buttonhandler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  292. {
  293.   ScanData *scan_data = (ScanData *)handle;
  294.   char *new_ptr;
  295.  
  296.   if(scan_data->phase != SCAN_PHASE_ERROR) {
  297.     switch(id_block->self_component) {
  298.       case SCAN_ABORT: /* Abort */
  299.         RE(toolbox_delete_object(0,scan_data->window_id))
  300.         return 1; /* claim event */
  301.  
  302.       case SCAN_FOURTHBUTTON: /* Pause/Continue */
  303.         if(scan_data->phase == SCAN_PHASE_PAUSE) {
  304.           /* unpause it */
  305.           RE(actionbutton_set_text(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanBPause")))
  306.           RE(gadget_set_help_message(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanHPause")))
  307.           RE(button_set_value(0, scan_data->window_id, SCAN_ACTIVITY, scan_data->return_action))
  308.           scan_data->phase = scan_data->return_phase;
  309.           RE(RoundRobin_register(_Scan_pollhandler, handle)) /* resume null-polling */
  310.         }
  311.         else {
  312.           /* pause it */
  313.           RE(actionbutton_set_text(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanBCont")))
  314.           RE(gadget_set_help_message(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanHCont")))
  315.           RE(button_get_value(0, scan_data->window_id, SCAN_ACTIVITY, scan_data->return_action, sizeof(scan_data->return_action), NULL))
  316.           RE(button_set_value(0, scan_data->window_id, SCAN_ACTIVITY, (char *)msgs_lookup("ScanTPaused")))
  317.           scan_data->return_phase = scan_data->phase;
  318.           scan_data->phase = SCAN_PHASE_PAUSE;
  319.           RoundRobin_deregister(_Scan_pollhandler, handle); /* cease null-polling */
  320.         }
  321.         return 1; /* claim event */
  322.  
  323.       default:
  324.         return 0; /* pass event on */
  325.     }
  326.   }
  327.   else {
  328.     /* Houston, we have a problem */
  329.     switch(id_block->self_component) {
  330.       case SCAN_ABORT:/* Abort */
  331.         RE(toolbox_delete_object(0,scan_data->window_id))
  332.         return 1; /* claim event */
  333.  
  334.       case SCAN_SKIP:/* Skip to next file */
  335.         if(!scan_data->single_file) {
  336.           _Scan_displayprogress(scan_data);
  337.           scan_data->phase = SCAN_PHASE_SCANNING;
  338.           RE(RoundRobin_register(_Scan_pollhandler, handle)) /* resume null-polling */
  339.         }
  340.         return 1; /* claim event */
  341.  
  342.       case SCAN_RESTART:/* Restart */
  343.         if(!scan_data->single_file) {
  344.           scan_data->level = 0;
  345.           scan_data->position[scan_data->level] = 0;
  346.           scan_data->numchecked = 0;
  347.           scan_data->numoutput = 0;
  348.           new_ptr = copystring("");
  349.           if(new_ptr == NULL)
  350.             return 1; /* claim event */
  351.           free(scan_data->relativedir);
  352.           scan_data->relativedir = new_ptr;
  353.           _Scan_displayprogress(scan_data);
  354.           _Scan_updatewindow(scan_data,msgs_lookup("ScanTOpen"),scan_data->loadroot);
  355.         RE(button_set_value(0, scan_data->window_id, SCAN_CONVERTED, "0"))
  356.  
  357.           scan_data->phase = SCAN_PHASE_SCANNING;
  358.           RE(RoundRobin_register(_Scan_pollhandler, handle)) /* resume null-polling */
  359.         }
  360.         return 1; /* claim event */
  361.  
  362.       case SCAN_FOURTHBUTTON:/* Retry */
  363.         if(scan_data->single_file) {
  364.           scan_data->thread_state = NULL;
  365.           scan_data->phase = SCAN_PHASE_LOAD;
  366.           scan_data->numchecked = 0;
  367.           scan_data->numoutput = 0;
  368.         } else {
  369.           scan_data->numchecked = scan_data->retry_numchecked;
  370.           scan_data->numoutput = scan_data->retry_numoutput;
  371.           scan_data->position[scan_data->level] = scan_data->retry_position;
  372.           scan_data->phase = SCAN_PHASE_SCANNING;
  373.         }
  374.         _Scan_displayprogress(scan_data);
  375.         RE(RoundRobin_register(_Scan_pollhandler, handle)) /* resume null-polling */
  376.         return 1; /* claim event */
  377.  
  378.       default:
  379.         return 0; /* event not handled */
  380.     }
  381.   }
  382. }
  383.  
  384. /* ----------------------------------------------------------------------- */
  385.  
  386. static int _Scan_deletedhandler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  387. {
  388.   ScanData *scan_data = (ScanData *)handle;
  389.  
  390.   if(scan_data->phase != SCAN_PHASE_ERROR
  391.   && scan_data->phase != SCAN_PHASE_PAUSE)
  392.     RoundRobin_deregister(_Scan_pollhandler, handle); /* if we were null polling then stop */
  393.  
  394.   RE(event_deregister_toolbox_handler(id_block->self_id, ActionButton_Selected, _Scan_buttonhandler, handle))
  395.   RE(event_deregister_toolbox_handler(id_block->self_id, Toolbox_ObjectDeleted, _Scan_deletedhandler, handle))
  396.  
  397.   RE(ViewsMenu_remove(id_block->self_id))
  398.  
  399.   /* free all memory for this op */
  400.   if(scan_data->buffer != NULL)
  401.     flex_free((flex_ptr)&scan_data->buffer);
  402.  
  403.   free(scan_data->position);
  404.   free(scan_data->loadpath);
  405.   free(scan_data->savepath);
  406.   free(scan_data->relativedir);
  407.  
  408.   /* and finally... */
  409.   free(scan_data->loadroot);
  410.   free(scan_data->saveroot);
  411.  
  412.   /* free state and close down any running thread */
  413.   if(scan_data->thread_state != NULL) {
  414.     if(scan_data->phase == SCAN_PHASE_LOAD 
  415.     || (scan_data->phase == SCAN_PHASE_PAUSE && scan_data->return_phase == SCAN_PHASE_LOAD)
  416.     || scan_data->phase == SCAN_PHASE_SAVE
  417.     || (scan_data->phase == SCAN_PHASE_PAUSE && scan_data->return_phase == SCAN_PHASE_SAVE))
  418.       abort_file_op(&scan_data->thread_state);
  419.   }
  420.   free(scan_data);
  421.  
  422.   return 1;  /* claim event */
  423. }
  424.  
  425. /* ----------------------------------------------------------------------- */
  426.  
  427. static void _Scan_displayerror(ScanData *scan_data, char *errormessage)
  428. {
  429.   /* Opens progress window to centre of screen at expanded size and sets up buttons */
  430.   WindowShowObjectBlock wsob;
  431.   int cernX,cernY;
  432.   
  433.   RE(showgadget(scan_data->window_id, SCAN_SKIP))
  434.   RE(showgadget(scan_data->window_id, SCAN_RESTART))
  435.  
  436.   /* Can't 'Skip' if doing single file (nowhere to skip to),
  437.   or stuck at end of directory (error leaving it),
  438.   or if scan_data->position wasn't updated (error calling OS_GBPB) */
  439.   RE(set_gadget_faded((scan_data->single_file || scan_data->position[scan_data->level] == -1 || scan_data->position[scan_data->level] == scan_data->retry_position), scan_data->window_id, SCAN_SKIP))
  440.  
  441.   /* Can't 'Restart' if doing single file */
  442.   RE(set_gadget_faded(scan_data->single_file, scan_data->window_id, SCAN_RESTART))
  443.  
  444.   RE(actionbutton_set_text(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanBRetry")))
  445.   RE(gadget_set_help_message(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanHRetry")))
  446.   RE(button_set_value(0, scan_data->window_id, SCAN_MESSAGE, errormessage))
  447.  
  448.   /* Alter visible area and centre */
  449.   RE(getscreencentre(&cernX, &cernY))
  450.   wsob.visible_area.xmin = cernX - (736/2);
  451.   wsob.visible_area.ymax = cernY + (596/2);
  452.   wsob.visible_area.xmax = cernX + (736/2);
  453.   wsob.visible_area.ymin = cernY - (596/2);
  454.   wsob.xscroll = 0;wsob.yscroll = 0;
  455.   wsob.behind = -1; /* top of stack */
  456.   RE(ViewsMenu_show_object(0, scan_data->window_id, Toolbox_ShowObject_FullSpec, &wsob, 0, 0))
  457.   /* N.B. consider the fact that window may be iconised */
  458. }
  459.  
  460. /* ----------------------------------------------------------------------- */
  461.  
  462. static void _Scan_displayprogress(ScanData *scan_data)
  463. {
  464.   WimpGetWindowInfoBlock windowinfo;
  465.   WindowShowObjectBlock wsob;
  466.  
  467.   /* Reduces progress window to normal size, restores normal buttons */
  468.   RE(hidegadget(scan_data->window_id, SCAN_SKIP))
  469.   RE(hidegadget(scan_data->window_id, SCAN_RESTART))
  470.  
  471.   if(scan_data->phase == SCAN_PHASE_PAUSE) {
  472.     RE(actionbutton_set_text(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanBCont")))
  473.     RE(gadget_set_help_message(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanHCont")))
  474.   }
  475.   else {
  476.     RE(actionbutton_set_text(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanBPause")))
  477.     RE(gadget_set_help_message(0, scan_data->window_id, SCAN_FOURTHBUTTON, (char *)msgs_lookup("ScanHPause")))
  478.   }
  479.  
  480.   /* Alter visible area but not position */
  481.   RE(window_get_wimp_handle(0, scan_data->window_id, &windowinfo.window_handle))
  482.   RE(wimp_get_window_info_no_icon_data(&windowinfo))
  483.   wsob.visible_area.xmin = windowinfo.window_data.visible_area.xmin;
  484.   wsob.visible_area.ymax = windowinfo.window_data.visible_area.ymax;
  485.   wsob.visible_area.xmax = windowinfo.window_data.visible_area.xmin + 620;
  486.   wsob.visible_area.ymin = windowinfo.window_data.visible_area.ymax - 252;
  487.   wsob.xscroll = 60;
  488.   wsob.yscroll = 0;
  489.   wsob.behind = windowinfo.window_data.behind;
  490.   RE(toolbox_show_object(0, scan_data->window_id, Toolbox_ShowObject_FullSpec, &wsob, 0, 0))
  491.   /* N.B. Window can never be iconised at this point (function called when just created, or as result of button press) */
  492. }
  493.  
  494. /* ----------------------------------------------------------------------- */
  495.  
  496. static void _Scan_updatewindow(ScanData *scan_data, const char *action, const char *filepath)
  497. {
  498.   /* Only update action text if not the same */
  499.   {
  500.     char current_action[16];
  501.     RE(button_get_value(0, scan_data->window_id, SCAN_ACTIVITY, current_action, sizeof(current_action), NULL))
  502.     if(strcmp(current_action, action) != 0)
  503.       RE(button_set_value(0, scan_data->window_id, SCAN_ACTIVITY, (char *)action))
  504.   }
  505.  
  506.   /* Whereas file paths are v. unlikely to be the same (impossible?) */
  507.   RE(button_set_value(0, scan_data->window_id, SCAN_PATH, (char *)filepath))
  508. }
  509.  
  510. /* ----------------------------------------------------------------------- */
  511.  
  512. static int _Scan_scanforfile(ScanData *scan_data) {
  513.   OSGBPBblock GBPB_buffer;
  514.   _kernel_swi_regs regs;
  515.  
  516.   scan_data->retry_position = scan_data->position[scan_data->level];
  517.   scan_data->retry_numchecked = scan_data->numchecked;
  518.   scan_data->retry_numoutput = scan_data->numoutput;
  519.  
  520.   /* construct input and output directory paths */
  521. #ifndef OLD_SCL_STUBS
  522.   char input_dir[strlen(scan_data->loadroot)+1+strlen(scan_data->relativedir)+1];
  523.   char output_dir[strlen(scan_data->saveroot)+1+strlen(scan_data->relativedir)+1];
  524. #else
  525.   char *input_dir = malloc(strlen(scan_data->loadroot)+1+strlen(scan_data->relativedir)+1);
  526.   if(input_dir == NULL)
  527.     RG_RETV("NoMem", SCAN_PHASE_ERROR)
  528.  
  529.   char *output_dir = malloc(strlen(scan_data->saveroot)+1+strlen(scan_data->relativedir)+1);
  530.   if(output_dir == NULL) {
  531.     free(input_dir);
  532.     RG_RETV("NoMem", SCAN_PHASE_ERROR)
  533.   }
  534. #endif
  535.  
  536.   if(strcmp(scan_data->relativedir, "") != 0) {
  537.     sprintf(input_dir, "%s.%s", scan_data->loadroot, scan_data->relativedir);
  538.     sprintf(output_dir, "%s.%s", scan_data->saveroot, scan_data->relativedir);
  539.   }
  540.   else {
  541.     strcpy(input_dir, scan_data->loadroot);
  542.     strcpy(output_dir, scan_data->saveroot);
  543.   }
  544.  
  545.   /* get next file */
  546.   regs.r[0] = 12; /* read entries and full info with filetype from given dir */
  547.   regs.r[1] = (int)input_dir; /* pointer to dir name */
  548.   regs.r[2] = (int)&GBPB_buffer; /* pointer to buffer */
  549.   regs.r[3] = 1; /* objects to read */
  550.   regs.r[4] = scan_data->position[scan_data->level]; /* where to start */
  551.   regs.r[5] = sizeof(OSGBPBblock); /* buffer length */
  552.   regs.r[6] = 0; /* any filename */
  553.   if(E(_kernel_swi(OS_GBPB, ®s, ®s))) {
  554. #ifdef OLD_SCL_STUBS
  555.     free(input_dir);
  556.     free(output_dir);
  557. #endif
  558.     return SCAN_PHASE_ERROR;
  559.   }
  560.   scan_data->position[scan_data->level] = regs.r[4];
  561.  
  562.   /* check for end of directory */
  563.   if(regs.r[4] == -1) {
  564.     /* =============================================== */
  565.     /* Reached end of this directory, so go up a level */
  566.  
  567.     _Scan_updatewindow(scan_data, msgs_lookup("ScanTLeave"), output_dir);
  568. #ifdef OLD_SCL_STUBS
  569.     free(input_dir);
  570.     free(output_dir);
  571. #endif
  572.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 0))
  573.     if(scan_data->level == 0) {
  574.       /* in root dir, so finish */
  575.       RE(toolbox_delete_object(0, scan_data->window_id))
  576.       return SCAN_PHASE_IDLE;
  577.     }
  578.  
  579.     /* WE HAVE TO BE VERY CAREFUL NOT TO BUGGER UP RELATIVEDIR OR POSITION[], IN ANY CIRCUMSTANCES! */
  580.     {
  581.       char *newstring;
  582.       {
  583.         /* Find last full-stop in directory pathname (relative to root) */
  584.         int stoplen;
  585.         char *laststop = strrchr(scan_data->relativedir, '.');
  586.         if(laststop == NULL)
  587.           /* Could not find full-stop so relativedir is just leafname */
  588.           laststop = scan_data->relativedir;
  589.         stoplen = (unsigned int)laststop - (unsigned int)scan_data->relativedir;
  590.         newstring = malloc(stoplen + 1);
  591.         if(newstring == NULL)
  592.           RG_RETV("NoMem", SCAN_PHASE_ERROR)
  593.         strncpy(newstring, scan_data->relativedir, stoplen);
  594.         laststop = (char *)((unsigned int)newstring + stoplen);
  595.         *laststop = 0;
  596.       }
  597.  
  598.       /* Retract array holding our position on each level */
  599.       {
  600.         int *newptr;
  601.         newptr = realloc(scan_data->position, (scan_data->level-1+1) * sizeof(int));
  602.         if(newptr == NULL) {
  603.           free(newstring);
  604.           RG_RETV("NoMem", SCAN_PHASE_ERROR)
  605.         }
  606.  
  607.         /* Now we're absolutely certain we can proceed up tree, its safe to bugger around with position and relativedir */
  608.         scan_data->level--;
  609.         free(scan_data->relativedir);
  610.         scan_data->relativedir = newstring;
  611.         scan_data->position = newptr;
  612.       }
  613.     }
  614.  
  615.     return SCAN_PHASE_SCANNING;
  616.   } /* if end of directory */
  617.  
  618.   /* ================================================================ */
  619.   /* There are further objects to scan within current directory level */
  620.   if(regs.r[3] != 1) {
  621. #ifdef OLD_SCL_STUBS
  622.     free(input_dir);
  623.     free(output_dir);
  624. #endif
  625.     R_RETV("IntErrScan", SCAN_PHASE_ERROR)
  626.   }
  627.  
  628.   free(scan_data->loadpath);
  629.   scan_data->loadpath = malloc(strlen(input_dir)+1+strlen(GBPB_buffer.filename)+1);
  630.   free(scan_data->savepath);
  631.   scan_data->savepath = malloc(strlen(output_dir)+1+strlen(GBPB_buffer.filename)+1);
  632.   if(scan_data->loadpath == NULL || scan_data->savepath == NULL) {
  633. #ifdef OLD_SCL_STUBS
  634.     free(input_dir);
  635.     free(output_dir);
  636. #endif
  637.     RG_RETV("NoMem", SCAN_PHASE_ERROR)
  638.   }
  639.  
  640.   /* make full path for file to load as */
  641.   sprintf(scan_data->loadpath, "%s.%s", input_dir, GBPB_buffer.filename);
  642. #ifdef OLD_SCL_STUBS
  643.   free(input_dir);
  644. #endif
  645.  
  646.   /* make full path for file to save as */
  647.   sprintf(scan_data->savepath, "%s.%s", output_dir, GBPB_buffer.filename);
  648. #ifdef OLD_SCL_STUBS
  649.   free(output_dir);
  650. #endif
  651.  
  652.   switch(GBPB_buffer.objecttype) {
  653.  
  654.     case 1:
  655.     case 3:{/* object is file (image files are treated as normal files) */
  656.       scan_data->numchecked++;
  657.       /* check whether we should load file */
  658.       bool is_comp = false;
  659.       for(int i = 0; i < 11 && !is_comp; i++) {
  660.         if(GBPB_buffer.filetype == fednet_filetypes[i])
  661.           is_comp = true; /* Whew! - decompressable */
  662.       }
  663.  
  664.       if((is_comp && !scan_data->compress) || (!is_comp && scan_data->compress)) {
  665.         scan_data->thread_state = NULL;
  666.         return SCAN_PHASE_LOAD;
  667.       }
  668.       else {
  669.         /* file of no interest */
  670.         _Scan_updatewindow(scan_data, msgs_lookup("ScanTIgnore"), scan_data->loadpath);
  671.         RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 0))
  672.         return SCAN_PHASE_SCANNING;
  673.       }
  674.       }break;
  675.  
  676.     case 2:/* Object is directory - go down a level */
  677.       _Scan_updatewindow(scan_data, msgs_lookup("ScanTOpen"), scan_data->loadpath);
  678.        RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 0))
  679.  
  680.       /* WE HAVE TO BE VERY CAREFUL NOT TO BUGGER UP RELATIVEDIR OR POSITION[], IN ANY CIRCUMSTANCES! */
  681.       {
  682.         char *newstring = malloc(strlen(scan_data->relativedir) + 1 + strlen(GBPB_buffer.filename) + 1);
  683.         if(newstring == NULL)
  684.           RG_RETV("NoMem", SCAN_PHASE_ERROR)
  685.         if(strcmp(scan_data->relativedir, "") != 0)
  686.           sprintf(newstring, "%s.%s", scan_data->relativedir, GBPB_buffer.filename);
  687.         else
  688.           strcpy(newstring, GBPB_buffer.filename);
  689.  
  690.         /* Extend array holding our position on each level */
  691.         {
  692.           int *newptr = realloc(scan_data->position, (scan_data->level+1+1) * sizeof(int));
  693.           if(newptr == NULL) {
  694.             free(newstring);
  695.             RG_RETV("NoMem", SCAN_PHASE_ERROR)
  696.           }
  697.           /* Now we're absolutely certain we can proceed down tree, its safe to bugger around with position and relativedir */
  698.           scan_data->level++;
  699.           free(scan_data->relativedir);
  700.           scan_data->relativedir = newstring;
  701.           scan_data->position = newptr;
  702.           scan_data->position[scan_data->level] = 0; /* start at beginning of dir */
  703.         }
  704.       }
  705.       break;
  706.  
  707.   } /* what object type */
  708.   return SCAN_PHASE_SCANNING;
  709. }
  710.  
  711. /* ----------------------------------------------------------------------- */
  712.  
  713. static int _Scan_loadfile(ScanData *scan_data, const volatile bool *time_up)
  714. {
  715.   _kernel_oserror *err;
  716.  
  717.   {
  718.     char *path;
  719.     if(scan_data->single_file)
  720.       path = scan_data->loadroot;
  721.     else
  722.       path = scan_data->loadpath;
  723.       
  724.     if(scan_data->thread_state == NULL) {
  725.       _Scan_updatewindow(scan_data, msgs_lookup("ScanTLoad"), path);
  726.       RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 0))
  727.       RE(slider_set_colour(0, scan_data->window_id, SCAN_PROGRESS, 10, 0))
  728.     }
  729.  
  730.     if(scan_data->compress)
  731.       /* Load plain file */
  732.       err = load_fileM(path, (flex_ptr)&scan_data->buffer, time_up, &scan_data->thread_state, false);
  733.     else
  734.       /* Load compressed file */
  735.       err = load_compressedM(path, (flex_ptr)&scan_data->buffer, time_up, &scan_data->thread_state);
  736.   }
  737.   if(err != NULL) {
  738.     scan_data->buffer = NULL;
  739.     err_report(err->errnum, msgs_lookup_sub1("LoadFail", err->errmess));
  740.     return SCAN_PHASE_ERROR; /* fail */
  741.   }
  742.  
  743.   if(scan_data->thread_state == NULL) {
  744.     /* Have finished decompression */
  745. #ifndef NDEBUG
  746.     if(!scan_data->compress)
  747.       _kernel_oscli("report Have finished loading");
  748. #endif
  749.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 100))
  750.     return SCAN_PHASE_SAVE; /* success */
  751.   }
  752.   else {
  753.     /* We will have to come back another time */
  754.     unsigned int perc;
  755. #ifndef NDEBUG
  756.    _kernel_oscli("report Loading incomplete");
  757. #endif
  758.     if(scan_data->compress)
  759.       perc = get_loadsave_perc(&scan_data->thread_state);
  760.     else
  761.       perc = get_decomp_perc(&scan_data->thread_state);
  762.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, perc))
  763.     return SCAN_PHASE_LOAD; /* success */
  764.   }
  765. }
  766.  
  767. /* ----------------------------------------------------------------------- */
  768.  
  769. static int _Scan_savefile(ScanData *scan_data, const volatile bool *time_up)
  770. {
  771.   char *path;
  772.   if(scan_data->single_file)
  773.     path = scan_data->saveroot;
  774.   else
  775.     path = scan_data->savepath;
  776.         
  777.   if(scan_data->thread_state == NULL) {
  778.     _Scan_updatewindow(scan_data, msgs_lookup("ScanTSave"), path);
  779.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 0))
  780.     RE(slider_set_colour(0, scan_data->window_id, SCAN_PROGRESS, 11, 0))
  781.   
  782.     /* Ensure that entirety of output file path exists */
  783.     if(!scan_data->single_file) {
  784.       char *ourdirstart, *chopsubdir;
  785.       _kernel_osfile_block kosfb;
  786.   
  787.       /* saveroot = "RAM::0.$.Landscapes"
  788.          relativedir = "Planets"
  789.          savepath = "RAM::0.$.Landscapes.Planets.Earth" */
  790.       ourdirstart = (char *)(
  791.         (unsigned int)scan_data->savepath
  792.         + ((unsigned int)strrchr(scan_data->saveroot, '.')+1 - (unsigned int)scan_data->saveroot)
  793.       ); /* find saveroot leaf ("Landscapes") in savepath */
  794.       /* ourdirstart = "Landscapes.Planets.Earth" */
  795.       chopsubdir = strchr(ourdirstart, '.'); /* end of head subdirectory */
  796.       while(chopsubdir != NULL) {
  797.         *chopsubdir=0;
  798.         /* savepath = {"RAM::0.$.Landscapes", "RAM::0.$.Landscapes.Planets"} */
  799.     
  800.         /* Create subdirectory */
  801.         kosfb.start = 0; /* default number of entries */
  802.         if(_kernel_osfile(8, scan_data->savepath, &kosfb) == _kernel_ERROR) {
  803.           _kernel_oserror *e = _kernel_last_oserror();
  804.           err_report(e->errnum, msgs_lookup_sub1("DirFail", e->errmess));
  805.           return SCAN_PHASE_ERROR; /* failure */
  806.         }
  807.  
  808.         *chopsubdir='.'; /* don't vandalise savepath */
  809.         chopsubdir = strchr((char *)((unsigned int)chopsubdir + 1), '.'); /* find end of next subdirectory */
  810.       }
  811.     }
  812.   }
  813.  
  814.   /* Time to save */
  815.   {
  816.     _kernel_oserror *err;
  817.       
  818.     if(scan_data->compress)
  819.       /* Save as compressed file */
  820.       err = save_compressedM(path, scan_data->comp_type, (flex_ptr)&scan_data->buffer, time_up, &scan_data->thread_state);
  821.     else
  822.       /* Save as plain file */
  823.       err = save_fileM(path, FILETYPE_DATA, (flex_ptr)&scan_data->buffer, time_up, &scan_data->thread_state, false);
  824.  
  825.     if(err != NULL) {
  826.       err_report(err->errnum, msgs_lookup_sub1("SaveFail", err->errmess));
  827.       return SCAN_PHASE_ERROR; /* failure */
  828.     }
  829.   }
  830.   if(scan_data->thread_state == NULL) {
  831.     /* Have finished saving data */
  832. #ifndef NDEBUG
  833.     _kernel_oscli("report Have finished saving data");
  834. #endif
  835.     /* deallocate memory for output buffer */
  836.     flex_free((flex_ptr)&scan_data->buffer);
  837.     /* Update count of files output */
  838.     scan_data->numoutput++;
  839.     {
  840.       char num[16];
  841. #ifndef OLD_SCL_STUBS
  842.       snprintf(num, sizeof(num), "%d", scan_data->numoutput);
  843. #else
  844.       sprintf(num, "%d", scan_data->numoutput);
  845. #endif
  846.       RE(button_set_value(0, scan_data->window_id, SCAN_CONVERTED, num))
  847.     }
  848.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, 100))
  849.  
  850.     if(scan_data->single_file) {
  851.       RE(toolbox_delete_object(0, scan_data->window_id))
  852.       return SCAN_PHASE_IDLE; /* success */
  853.     } else
  854.       return SCAN_PHASE_SCANNING; /* success */
  855.   }
  856.   else {
  857.     /* We will have to come back another time */
  858.     unsigned int perc;
  859. #ifndef NDEBUG
  860.     _kernel_oscli("report Saving incomplete");
  861. #endif
  862.     if(scan_data->compress)
  863.       perc = get_comp_perc(&scan_data->thread_state);
  864.     else
  865.       perc = get_loadsave_perc(&scan_data->thread_state);
  866.     RE(slider_set_value(0, scan_data->window_id, SCAN_PROGRESS, perc))
  867.     return SCAN_PHASE_SAVE; /* success */
  868.   }
  869. }
  870.  
  871.